package auth

import (
	"encoding/json"
	"errors"
	"fmt"

	"github.com/boltdb/bolt"
)

const UserBucket = "users"

type boltStore struct {
	db *bolt.DB
}

func NewBoltStore(path string) (UserStore, error) {
	return newBoltStore(path)
}

func newBoltStore(path string) (*boltStore, error) {
	db, err := bolt.Open(path, 0600, nil)
	if err != nil {
		return nil, err
	}

	store := &boltStore{db: db}
	err = store.setup()

	return store, err
}

func (store *boltStore) setup() error {
	return store.db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte(UserBucket))
		return err
	})
}

func (store *boltStore) Close() {
	store.db.Close()
}

func (store *boltStore) CreateUser(u User) (User, error) {
	if u.Name == "" {
		return u, errors.New("Username cannot be empty")
	}

	u.ID = u.Name

	err := store.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(UserBucket))

		if len(b.Get([]byte(u.Name))) != 0 {
			return fmt.Errorf("User %s exists", u.Name)
		}

		data, err := json.Marshal(u)
		if err != nil {
			return err
		}
		b.Put([]byte(u.Name), data)

		return nil
	})

	u.Password = ""

	return u, err
}

func (store *boltStore) Authenticate(u User) (User, error) {
	err := store.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(UserBucket))
		data := b.Get([]byte(u.Name))
		if len(data) == 0 {
			return fmt.Errorf("Cannot find User %s", u.Name)
		}
		var user User
		err := json.Unmarshal(data, &user)
		if err != nil {
			return err
		}
		if user.Password != u.Password {
			return errors.New("Wrong password")
		}
		u = user
		return nil
	})

	u.Password = ""

	return u, err
}

func (store *boltStore) GetUserById(id string) (User, error) {
	var user User
	err := store.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(UserBucket))
		data := b.Get([]byte(id))
		if len(data) == 0 {
			return fmt.Errorf("Cannot find User %s", id)
		}
		return json.Unmarshal(data, &user)
	})

	user.Password = ""

	return user, err
}
